<!DOCTYPE html>
<html>

<head>

<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
<title>jd v3</title>


<style type="text/css">
*{margin:0;padding:0;}
body {
	font:13.34px helvetica,arial,freesans,clean,sans-serif;
	color:black;
	line-height:1.4em;
	background-color: #F8F8F8;
	padding: 0.7em;
}
p {
	margin:1em 0;
	line-height:1.5em;
}
table {
	font-size:inherit;
	font:100%;
	margin:1em;
}
table th{border-bottom:1px solid #bbb;padding:.2em 1em;}
table td{border-bottom:1px solid #ddd;padding:.2em 1em;}
input[type=text],input[type=password],input[type=image],textarea{font:99% helvetica,arial,freesans,sans-serif;}
select,option{padding:0 .25em;}
optgroup{margin-top:.5em;}
pre,code{font:12px Monaco,"Courier New","DejaVu Sans Mono","Bitstream Vera Sans Mono",monospace;}
pre {
	margin:1em 0;
	font-size:12px;
	background-color:#eee;
	border:1px solid #ddd;
	padding:5px;
	line-height:1.5em;
	color:#444;
	overflow:auto;
	-webkit-box-shadow:rgba(0,0,0,0.07) 0 1px 2px inset;
	-webkit-border-radius:3px;
	-moz-border-radius:3px;border-radius:3px;
}
pre code {
	padding:0;
	font-size:12px;
	background-color:#eee;
	border:none;
}
code {
	font-size:12px;
	background-color:#f8f8ff;
	color:#444;
	padding:0 .2em;
	border:1px solid #dedede;
}
img{border:0;max-width:100%;}
abbr{border-bottom:none;}
a{color:#4183c4;text-decoration:none;}
a:hover{text-decoration:underline;}
a code,a:link code,a:visited code{color:#4183c4;}
h2,h3{margin:1em 0;}
h1,h2,h3,h4,h5,h6{border:0;}
h1{font-size:170%;border-top:4px solid #aaa;padding-top:.5em;margin-top:1.5em;}
h1:first-child{margin-top:0;padding-top:.25em;border-top:none;}
h2{font-size:150%;margin-top:1.5em;border-top:4px solid #e0e0e0;padding-top:.5em;}
h3{margin-top:1em;}
hr{border:1px solid #ddd;}
ul{margin:1em 0 1em 2em;}
ol{margin:1em 0 1em 2em;}
ul li,ol li{margin-top:.5em;margin-bottom:.5em;}
ul ul,ul ol,ol ol,ol ul{margin-top:0;margin-bottom:0;}
blockquote{margin:1em 0;border-left:5px solid #ddd;padding-left:.6em;color:#555;}
dt{font-weight:bold;margin-left:1em;}
dd{margin-left:2em;margin-bottom:1em;}
sup {
    font-size: 0.83em;
    vertical-align: super;
    line-height: 0;
}
kbd {
  display: inline-block;padding: 3px 5px;font-size: 11px;line-height: 10px;color: #555;vertical-align: middle;background-color: #fcfcfc;border: solid 1px #ccc;border-bottom-color: #bbb;border-radius: 3px;box-shadow: inset 0 -1px 0 #bbb;
}
* {
	-webkit-print-color-adjust: exact;
}
@media screen and (min-width: 914px) {
    body {
        width: 854px;
        margin:0 auto;
    }
}
@media print {
	table, pre {
		page-break-inside: avoid;
	}
	pre {
		word-wrap: break-word;
	}
}
</style>

<style type="text/css">
/**
 * prism.js default theme for JavaScript, CSS and HTML
 * Based on dabblet (http://dabblet.com)
 * @author Lea Verou
 */

code[class*="language-"],
pre[class*="language-"] {
	color: black;
	background: none;
	text-shadow: 0 1px white;
	font-family: Consolas, Monaco, 'Andale Mono', 'Ubuntu Mono', monospace;
	text-align: left;
	white-space: pre;
	word-spacing: normal;
	word-break: normal;
	word-wrap: normal;
	line-height: 1.5;

	-moz-tab-size: 4;
	-o-tab-size: 4;
	tab-size: 4;

	-webkit-hyphens: none;
	-moz-hyphens: none;
	-ms-hyphens: none;
	hyphens: none;
}

pre[class*="language-"]::-moz-selection, pre[class*="language-"] ::-moz-selection,
code[class*="language-"]::-moz-selection, code[class*="language-"] ::-moz-selection {
	text-shadow: none;
	background: #b3d4fc;
}

pre[class*="language-"]::selection, pre[class*="language-"] ::selection,
code[class*="language-"]::selection, code[class*="language-"] ::selection {
	text-shadow: none;
	background: #b3d4fc;
}

@media print {
	code[class*="language-"],
	pre[class*="language-"] {
		text-shadow: none;
	}
}

/* Code blocks */
pre[class*="language-"] {
	padding: 1em;
	margin: .5em 0;
	overflow: auto;
}

:not(pre) > code[class*="language-"],
pre[class*="language-"] {
	background: #f5f2f0;
}

/* Inline code */
:not(pre) > code[class*="language-"] {
	padding: .1em;
	border-radius: .3em;
	white-space: normal;
}

.token.comment,
.token.prolog,
.token.doctype,
.token.cdata {
	color: slategray;
}

.token.punctuation {
	color: #999;
}

.namespace {
	opacity: .7;
}

.token.property,
.token.tag,
.token.boolean,
.token.number,
.token.constant,
.token.symbol,
.token.deleted {
	color: #905;
}

.token.selector,
.token.attr-name,
.token.string,
.token.char,
.token.builtin,
.token.inserted {
	color: #690;
}

.token.operator,
.token.entity,
.token.url,
.language-css .token.string,
.style .token.string {
	color: #a67f59;
	background: hsla(0, 0%, 100%, .5);
}

.token.atrule,
.token.attr-value,
.token.keyword {
	color: #07a;
}

.token.function {
	color: #DD4A68;
}

.token.regex,
.token.important,
.token.variable {
	color: #e90;
}

.token.important,
.token.bold {
	font-weight: bold;
}
.token.italic {
	font-style: italic;
}

.token.entity {
	cursor: help;
}
</style>


</head>

<body>

<p>参考代码：<a href="http://git.oschina.net/crossin/crawler/blob/master/ex5_pyspider_jd_v3.py" target="_blank">http://git.oschina.net/crossin/crawler/blob/master/ex5_pyspider_jd_v3.py</a></p>

<h2 id="toc_0">1. pyspider 调试环境</h2>

<p>pyspider 在调试环境中提供了多个视图，满足开发调试时的常见需求。</p>

<ul>
<li><p><strong>web 视图</strong> 在 web 控制台以浏览器模式显示请求页面</p></li>
<li><p><strong>html 视图</strong> 显示请求页面的 HTML 代码</p></li>
<li><p><strong>follows 视图</strong> 查看当前响应中，产生的所有待抓取请求</p></li>
<li><p><strong>messages 视图</strong> 显示来自 <code>self.send_message</code> API 接口的消息</p></li>
<li><p><strong>CSS 选择器助手（Enable CSS Selector Helper）</strong> 开启后，在 web 视图中通过单击可获取节点的 css 规则，并可以自动插入到代码脚本中。</p></li>
</ul>

<h3 id="toc_1">参考资料</h3>

<ol>
<li><a href="http://www.pyspider.cn/book/pyspider/Environment-14.html" target="_blank">环境 - pyspider 中文文档</a></li>
</ol>

<h2 id="toc_2">2. CSS 选择器</h2>

<p>CSS（层叠样式表，Cascading Style Sheets）定义了在网页上如何显示 HTML 元素。</p>

<p>CSS 选择器用于在页面代码中选择需要添加样式的指定元素。</p>

<p>在爬虫采集数据时，我们常会利用 CSS 选择器来定位页面上的信息。</p>

<h3 id="toc_3">常见选择器规则</h3>

<table>
<thead>
<tr>
<th style="text-align: center">选择器</th>
<th style="text-align: center">示例</th>
<th style="text-align: left">描述</th>
</tr>
</thead>

<tbody>
<tr>
<td style="text-align: center">.class</td>
<td style="text-align: center">.intro</td>
<td style="text-align: left">选择 class=&quot;intro&quot; 的所有元素</td>
</tr>
<tr>
<td style="text-align: center">#id</td>
<td style="text-align: center">#firstname</td>
<td style="text-align: left">选择 id=&quot;firstname&quot; 的所有元素</td>
</tr>
<tr>
<td style="text-align: center">*</td>
<td style="text-align: center">*</td>
<td style="text-align: left">选择所有元素</td>
</tr>
<tr>
<td style="text-align: center">element</td>
<td style="text-align: center">p</td>
<td style="text-align: left">选择所有 &lt;p&gt; 元素</td>
</tr>
<tr>
<td style="text-align: center">element element</td>
<td style="text-align: center">div p</td>
<td style="text-align: left">择 &lt;div&gt; 元素内部的所有 &lt;p&gt; 元素</td>
</tr>
<tr>
<td style="text-align: center">element&gt;element</td>
<td style="text-align: center">div&gt;p</td>
<td style="text-align: left">选择父元素为 &lt;div&gt; 元素的所有 &lt;p&gt; 元素</td>
</tr>
<tr>
<td style="text-align: center">[attribute]</td>
<td style="text-align: center">[target]</td>
<td style="text-align: left">选择带有 target 属性所有元素</td>
</tr>
<tr>
<td style="text-align: center">[attribute=value]</td>
<td style="text-align: center">[target=_blank]</td>
<td style="text-align: left">选择 target=&quot;_blank&quot; 的所有元素</td>
</tr>
</tbody>
</table>

<h3 id="toc_4">参考资料</h3>

<ol>
<li><a href="http://www.w3school.com.cn/css/css_intro.asp" target="_blank">CSS 简介 - w3school</a></li>
<li><a href="http://www.w3school.com.cn/cssref/css_selectors.asp" target="_blank">CSS 选择器参考手册 - w3school</a></li>
</ol>

<h2 id="toc_5">3. PyQuery</h2>

<p>PyQuery 类似于 XPath、BeautifulSoup，也是一个实现在 HTML 文档中定位元素的模块。</p>

<p>PyQuery 是 Python 仿照 jQuery 的严格实现，语法与 jQuery 几乎完全相同。对 jQuery 或 CSS 选择器语法熟悉的话，用起来会很顺手。</p>

<h3 id="toc_6">安装</h3>

<div><pre><code class="language-none">pip install pyquery</code></pre></div>

<h3 id="toc_7">示例</h3>

<p>4 种初始化方法：</p>

<div><pre><code class="language-python">&gt;&gt;&gt; from pyquery import PyQuery as pq
&gt;&gt;&gt; from lxml import etree
&gt;&gt;&gt; import urllib
&gt;&gt;&gt; d = pq(&quot;&lt;html&gt;&lt;/html&gt;&quot;)
&gt;&gt;&gt; d = pq(etree.fromstring(&quot;&lt;html&gt;&lt;/html&gt;&quot;))
&gt;&gt;&gt; d = pq(url=&#39;http://crossincode.com/&#39;)
&gt;&gt;&gt; d = pq(filename=path_to_html_file)</code></pre></div>

<p>初始化之后，d 变量就可以像 jQuery 中的 <code>$</code> 来使用，搜索文档中的元素。</p>

<p>测试文本：</p>

<div><pre><code class="language-none">&lt;div&gt;
  &lt;ul&gt;
    &lt;li class=&quot;item-0&quot;&gt;first item&lt;/li&gt;
    &lt;li class=&quot;item-1&quot;&gt;&lt;a href=&quot;link2.html&quot;&gt;second item&lt;/a&gt;&lt;/li&gt;
    &lt;li class=&quot;item-0 active&quot;&gt;&lt;a href=&quot;link3.html&quot;&gt;&lt;span class=&quot;bold&quot;&gt;third item&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
    &lt;li class=&quot;item-1 active&quot;&gt;&lt;a href=&quot;link4.html&quot;&gt;fourth item&lt;/a&gt;&lt;/li&gt;
    &lt;li class=&quot;item-0&quot;&gt;&lt;a href=&quot;link5.html&quot;&gt;fifth item&lt;/a&gt;&lt;/li&gt;
  &lt;/ul&gt;
&lt;/div&gt;</code></pre></div>

<p>简单示例代码：</p>

<div><pre><code class="language-python">from pyquery import PyQuery
doc = PyQuery(filename=&#39;hello.html&#39;)
print(doc.html())
print(type(doc))
li = doc(&#39;li&#39;)
print(type(li))
print(li.text())</code></pre></div>

<p>输出：</p>

<div><pre><code class="language-none">&lt;ul&gt;
  &lt;li class=&quot;item-0&quot;&gt;first item&lt;/li&gt;
  &lt;li class=&quot;item-1&quot;&gt;&lt;a href=&quot;link2.html&quot;&gt;second item&lt;/a&gt;&lt;/li&gt;
  &lt;li class=&quot;item-0 active&quot;&gt;&lt;a href=&quot;link3.html&quot;&gt;&lt;span class=&quot;bold&quot;&gt;third item&lt;/span&gt;&lt;/a&gt;&lt;/li&gt;
  &lt;li class=&quot;item-1 active&quot;&gt;&lt;a href=&quot;link4.html&quot;&gt;fourth item&lt;/a&gt;&lt;/li&gt;
  &lt;li class=&quot;item-0&quot;&gt;&lt;a href=&quot;link5.html&quot;&gt;fifth item&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;
 
&lt;class &#39;pyquery.pyquery.PyQuery&#39;&gt;
&lt;class &#39;pyquery.pyquery.PyQuery&#39;&gt;
first item second item third item fourth item fifth item</code></pre></div>

<h3 id="toc_8">pyspider Response.doc</h3>

<p>pyspider 中的选择器默认基于 PyQuery。通过 Response.doc 方法，使用 PyQuery 语法定位文档中的元素。返回值是一个 PyQuery 对象，其中所有链接会自动转换成完整地址。</p>

<h3 id="toc_9">参考资料</h3>

<ol>
<li><a href="https://mp.weixin.qq.com/s?__biz=MjM5MDEyMDk4Mw==&amp;mid=2650166474&amp;idx=2&amp;sn=6ef4c6c20dac04255fcb8f94c7829ff2&amp;chksm=be4b59b2893cd0a49f5b7a349b35b9eca0745e119806c2cb12001974aef2ef0ca2c8384665e1&amp;scene=0" target="_blank">以 jQuery 之名 - 爬虫利器 PyQuery</a></li>
<li><a href="http://cuiqingcai.com/2636.html" target="_blank">Python 爬虫利器六之 PyQuery 的用法 - 崔庆才</a></li>
<li><a href="https://pythonhosted.org/pyquery/" target="_blank">pyquery 官方文档</a></li>
<li><a href="http://www.pyspider.cn/book/pyspider/Response-17.html" target="_blank">Response - pyspider 中文文档</a></li>
</ol>

<h2 id="toc_10">4. pyspider 连接数据库</h2>

<p>pyspider 可以通过 WebUI 控制台查看和下载爬取结果。如果需要额外的保存方式，可以使用数据库 API，连接及操作数据库。</p>

<h3 id="toc_11">查看结果</h3>

<p>示例：</p>

<div><pre><code class="language-python">from pyspider.database import connect_database
resultdb = connect_database(&quot;sqlite+resultdb:///data/result.db&quot;)
for project in resultdb.projects:
    for result in resultdb.select(project):
        assert result[&#39;taskid&#39;]
        assert result[&#39;url&#39;]
        assert result[&#39;result&#39;]</code></pre></div>

<p>connect_database 的参数是数据库的连接地址，具体参考 pyspider 文档中说明。result[&#39;result&#39;] 就是爬虫脚本获取的数据。</p>

<h3 id="toc_12">保存结果</h3>

<p>重新 on_result 方法，可以不使用 resultdb，自定义其他的保存方式</p>

<div><pre><code class="language-python">def on_result(self, task, result):
    assert task[&#39;taskid&#39;]
    assert task[&#39;project&#39;]
    assert task[&#39;url&#39;]
    assert result
    # 你的保存代码
    # ...</code></pre></div>

<p>也可以继承 ResultWorker 类，然后在启动时或配置文件中将 result_worker 和 result_cls 设置成自定义的子类。</p>

<div><pre><code class="language-python">from pyspider.result import ResultWorker
class MyResultWorker(ResultWorker):
    def on_result(self, task, result):
        # ...</code></pre></div>

<p>启动：</p>

<div><pre><code class="language-none">pyspider result_worker --result-cls=my_result_worder</code></pre></div>

<p>配置文件：</p>

<div><pre><code class="language-none">{
  ...
  &quot;result_worker&quot;: {
    &quot;result_cls&quot;: &quot;my_result_worker.MyResultWorker&quot;
  }
  ...
} </code></pre></div>

<h3 id="toc_13">参考资料</h3>

<ol>
<li><a href="http://www.pyspider.cn/book/pyspider/Results-15.html" target="_blank">结果 - pyspider 中文文档</a></li>
<li><a href="http://www.pyspider.cn/book/pyspider/Deployment-6.html" target="_blank">部署 - pyspider 中文文档</a></li>
</ol>

<h2 id="toc_14">5. 短路逻辑</h2>

<p>在讨论 bool 运算的短路逻辑之前，你需要熟悉基本的 bool 运算。</p>

<p>布尔运算符有个特性：只有在需要求值时才进行求值。举例来说，表达式 <code>x and y</code> 需要两个变量都为真时才为真，所以如果 <code>x</code> 为假，表达式就会立刻返回 <code>False</code>，而不再理会 <code>y</code> 的值。所以，如果 <code>y</code> 是一个计算式或函数时，程序并不会对其进行运算和调用。</p>

<p>实际在程序中，如果 <code>x</code> 为假，表达式会直接返回 <code>x</code> 得值，否则返回 <code>y</code> 的值。这种行为被称为 <strong>短路逻辑（short-circuit logic）</strong> 或 <strong>惰性求值（lazy evaluaion）</strong>：表达式后面部分的值有时“被短路了”。</p>

<p>这种行为对于 or 来说也同样适用。在表达式 <code>x or y</code> 中，<code>x</code> 为真时，它直接返回 <code>x</code> 的值，这意味着之后的代码都不会执行。否则，返回 <code>y</code> 值。</p>

<p>利用这种技巧，可以简化一些判断代码。比如只有在获取到非空值时才做处理。</p>

<h3 id="toc_15">示例</h3>

<div><pre><code class="language-python">&gt;&gt;&gt; name = &#39;&#39;
&gt;&gt;&gt; print(name or &#39;unknown&#39;)  # 提供默认值
unknown

&gt;&gt;&gt; name = &#39;Crossin&#39;
&gt;&gt;&gt; print(name and name[0])  # 避免报错
C</code></pre></div>

<h3 id="toc_16">参考资料</h3>

<ol>
<li><a href="http://www.cnblogs.com/skyhacker/archive/2012/01/22/2328815.html" target="_blank">python 短路逻辑和条件表达式 - SkyHacker</a></li>
<li><a href="http://www.cnblogs.com/an9wer/p/5475551.html" target="_blank">python中 and 和 or 运算的核心思想 ——— 短路逻辑 - luser_run</a></li>
</ol>



<script type="text/javascript">
var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/\u00a0/g," ")},type:function(e){return Object.prototype.toString.call(e).match(/\[object (\w+)\]/)[1]},objId:function(e){return e.__id||Object.defineProperty(e,"__id",{value:++t}),e.__id},clone:function(e){var t=n.util.type(e);switch(t){case"Object":var a={};for(var r in e)e.hasOwnProperty(r)&&(a[r]=n.util.clone(e[r]));return a;case"Array":return e.map&&e.map(function(e){return n.util.clone(e)})}return e}},languages:{extend:function(e,t){var a=n.util.clone(n.languages[e]);for(var r in t)a[r]=t[r];return a},insertBefore:function(e,t,a,r){r=r||n.languages;var l=r[e];if(2==arguments.length){a=arguments[1];for(var i in a)a.hasOwnProperty(i)&&(l[i]=a[i]);return l}var o={};for(var s in l)if(l.hasOwnProperty(s)){if(s==t)for(var i in a)a.hasOwnProperty(i)&&(o[i]=a[i]);o[s]=l[s]}return n.languages.DFS(n.languages,function(t,n){n===r[e]&&t!=e&&(this[t]=o)}),r[e]=o},DFS:function(e,t,a,r){r=r||{};for(var l in e)e.hasOwnProperty(l)&&(t.call(e,l,e[l],a||l),"Object"!==n.util.type(e[l])||r[n.util.objId(e[l])]?"Array"!==n.util.type(e[l])||r[n.util.objId(e[l])]||(r[n.util.objId(e[l])]=!0,n.languages.DFS(e[l],t,l,r)):(r[n.util.objId(e[l])]=!0,n.languages.DFS(e[l],t,null,r)))}},plugins:{},highlightAll:function(e,t){var a={callback:t,selector:'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'};n.hooks.run("before-highlightall",a);for(var r,l=a.elements||document.querySelectorAll(a.selector),i=0;r=l[i++];)n.highlightElement(r,e===!0,a.callback)},highlightElement:function(t,a,r){for(var l,i,o=t;o&&!e.test(o.className);)o=o.parentNode;o&&(l=(o.className.match(e)||[,""])[1],i=n.languages[l]),t.className=t.className.replace(e,"").replace(/\s+/g," ")+" language-"+l,o=t.parentNode,/pre/i.test(o.nodeName)&&(o.className=o.className.replace(e,"").replace(/\s+/g," ")+" language-"+l);var s=t.textContent,u={element:t,language:l,grammar:i,code:s};if(!s||!i)return n.hooks.run("complete",u),void 0;if(n.hooks.run("before-highlight",u),a&&_self.Worker){var c=new Worker(n.filename);c.onmessage=function(e){u.highlightedCode=e.data,n.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(u.element),n.hooks.run("after-highlight",u),n.hooks.run("complete",u)},c.postMessage(JSON.stringify({language:u.language,code:u.code,immediateClose:!0}))}else u.highlightedCode=n.highlight(u.code,u.grammar,u.language),n.hooks.run("before-insert",u),u.element.innerHTML=u.highlightedCode,r&&r.call(t),n.hooks.run("after-highlight",u),n.hooks.run("complete",u)},highlight:function(e,t,r){var l=n.tokenize(e,t);return a.stringify(n.util.encode(l),r)},tokenize:function(e,t){var a=n.Token,r=[e],l=t.rest;if(l){for(var i in l)t[i]=l[i];delete t.rest}e:for(var i in t)if(t.hasOwnProperty(i)&&t[i]){var o=t[i];o="Array"===n.util.type(o)?o:[o];for(var s=0;s<o.length;++s){var u=o[s],c=u.inside,g=!!u.lookbehind,h=!!u.greedy,f=0,d=u.alias;u=u.pattern||u;for(var p=0;p<r.length;p++){var m=r[p];if(r.length>e.length)break e;if(!(m instanceof a)){u.lastIndex=0;var y=u.exec(m),v=1;if(!y&&h&&p!=r.length-1){var b=r[p+1].matchedStr||r[p+1],k=m+b;if(p<r.length-2&&(k+=r[p+2].matchedStr||r[p+2]),u.lastIndex=0,y=u.exec(k),!y)continue;var w=y.index+(g?y[1].length:0);if(w>=m.length)continue;var _=y.index+y[0].length,P=m.length+b.length;if(v=3,P>=_){if(r[p+1].greedy)continue;v=2,k=k.slice(0,P)}m=k}if(y){g&&(f=y[1].length);var w=y.index+f,y=y[0].slice(f),_=w+y.length,S=m.slice(0,w),O=m.slice(_),j=[p,v];S&&j.push(S);var A=new a(i,c?n.tokenize(y,c):y,d,y,h);j.push(A),O&&j.push(O),Array.prototype.splice.apply(r,j)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,l=0;r=a[l++];)r(t)}}},a=n.Token=function(e,t,n,a,r){this.type=e,this.content=t,this.alias=n,this.matchedStr=a||null,this.greedy=!!r};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var l={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==l.type&&(l.attributes.spellcheck="true"),e.alias){var i="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(l.classes,i)}n.hooks.run("wrap",l);var o="";for(var s in l.attributes)o+=(o?" ":"")+s+'="'+(l.attributes[s]||"")+'"';return"<"+l.tag+' class="'+l.classes.join(" ")+'" '+o+">"+l.content+"</"+l.tag+">"},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,l=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),l&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&document.addEventListener("DOMContentLoaded",n.highlightAll)),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
</script>

<script type="text/javascript">
Prism.languages.python={"triple-quoted-string":{pattern:/"""[\s\S]+?"""|'''[\s\S]+?'''/,alias:"string"},comment:{pattern:/(^|[^\\])#.*/,lookbehind:!0},string:/("|')(?:\\?.)*?\1/,"function":{pattern:/((?:^|\s)def[ \t]+)[a-zA-Z_][a-zA-Z0-9_]*(?=\()/g,lookbehind:!0},"class-name":{pattern:/(\bclass\s+)[a-z0-9_]+/i,lookbehind:!0},keyword:/\b(?:as|assert|async|await|break|class|continue|def|del|elif|else|except|exec|finally|for|from|global|if|import|in|is|lambda|pass|print|raise|return|try|while|with|yield)\b/,"boolean":/\b(?:True|False)\b/,number:/\b-?(?:0[bo])?(?:(?:\d|0x[\da-f])[\da-f]*\.?\d*|\.\d+)(?:e[+-]?\d+)?j?\b/i,operator:/[-+%=]=?|!=|\*\*?=?|\/\/?=?|<[<=>]?|>[=>]?|[&|^~]|\b(?:or|and|not)\b/,punctuation:/[{}[\];(),.:]/};
</script>


</body>

</html>
